package claim.io;

import java.io.IOException;
import java.io.Serializable;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFactorySpi;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureCollections;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.SchemaException;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Polygon;

import claim.geom.Feature;
import claim.geom.GeometryException;
import claim.geom.PolygonFeature;
import claim.geoobj.Boundary;
import claim.geoobj.Buffer;

public class ShapeIO {

	private static final Log LOG = LogFactory.getLog(Class.class.getName());
//	private SimpleFeatureType featureType;

	public ShapeIO() {
//		try {
//			this.featureType = DataUtilities.createType("feature", PolygonFeature.SIMPLE_FEATURE_TYPE);
//		} catch (SchemaException e) {
//			LOG.error("could not create feature type: " + e, e);
//		}
	}

	public Collection<PolygonFeature> readPolygonFeatures(URL url, String classAttribute) throws IOException {
		LOG.info("reading features from shapefile " + url);
		Collection<PolygonFeature> features = new HashSet<PolygonFeature>();
		FeatureIterator<SimpleFeature> iterator = null;
		
		try {
			// Connection parameters
			Map<String, Serializable> connectParameters = new HashMap<String, Serializable>();

			connectParameters.put("url", url);
			connectParameters.put("create spatial index", true);
			DataStore dataStore = DataStoreFinder.getDataStore(connectParameters);

			// we are now connected
			String[] typeNames = dataStore.getTypeNames();
			String typeName = typeNames[0];

			FeatureSource<SimpleFeatureType, SimpleFeature> featureSource;
			FeatureCollection<SimpleFeatureType, SimpleFeature> collection;

			featureSource = dataStore.getFeatureSource(typeName);
			collection = featureSource.getFeatures();
			iterator = collection.features();

			while (iterator.hasNext()) {
				SimpleFeature simpleFeature = iterator.next();
				features.add(new PolygonFeature(simpleFeature, classAttribute));
			}

		} catch (NullPointerException ex) {
			throw new IOException(ex);
		} finally {
			if (iterator != null) {
				iterator.close();
			}
		}

		return features;

	}

	public Map<String,Buffer> readBuffers(URL url, String attribute) throws IOException {
		LOG.info("reading buffers from shapefile " + url);
		Map<String,Buffer> geoms = new HashMap<String,Buffer>();
		FeatureIterator<SimpleFeature> iterator = null;
		
		try {
			// Connection parameters
			Map<String, Serializable> connectParameters = new HashMap<String, Serializable>();

			connectParameters.put("url", url);
			connectParameters.put("create spatial index", true);
			DataStore dataStore = DataStoreFinder.getDataStore(connectParameters);

			// we are now connected
			String[] typeNames = dataStore.getTypeNames();
			String typeName = typeNames[0];

			FeatureSource<SimpleFeatureType, SimpleFeature> featureSource;
			FeatureCollection<SimpleFeatureType, SimpleFeature> collection;

			featureSource = dataStore.getFeatureSource(typeName);
			collection = featureSource.getFeatures();
			iterator = collection.features();

			while (iterator.hasNext()) {
				SimpleFeature simpleFeature = iterator.next();
				String bufferId = (String) simpleFeature.getAttribute(attribute);
				String[] tokens = bufferId.split("\\.");
				Boundary bufferBoundary = new Boundary(tokens[1]);
				bufferBoundary.setOuter(true);
				Geometry geom = (Geometry) simpleFeature.getDefaultGeometry();
//				LinearRing shell = new GeometryFactory().createLinearRing(geom.getGeometryN(0).getCoordinates());
//				LinearRing[] holes = new LinearRing[geom.getNumGeometries()-1];				
//				for (int i = 1; i < geom.getNumGeometries(); i++){
//					Coordinate[] coords = geom.getGeometryN(i).getCoordinates();
//					holes[i] = new GeometryFactory().createLinearRing(coords);
//				}
//				Polygon polygon = new GeometryFactory().createPolygon(shell, holes);
//				Polygon polygon = (Polygon) geom.getGeometryN(0);
				bufferBoundary.putBoundary("", geom);
				Buffer buffer = new Buffer(bufferBoundary);
				String objId = tokens[0] + "." + tokens[1];
				geoms.put(objId, buffer);
			}

		} catch (NullPointerException ex) {
			throw new IOException(ex);
		} catch (GeometryException e) {
			throw new IOException(e);
		} finally {
			if (iterator != null) {
				iterator.close();
			}
		}

		return geoms;
		
	}
	
	public void writeFeatures(Collection<Feature> features, String featureTypeString, URL url)
			throws IOException {

		SimpleFeatureType featureType;
		try {
			featureType = DataUtilities.createType("feature", featureTypeString);
		} catch (SchemaException e) {
			LOG.error("could not create feature type: " + e, e);
			return;
		}
			
		DataStoreFactorySpi dataStoreFactory = new ShapefileDataStoreFactory();

		Map<String, Serializable> params = new HashMap<String, Serializable>();
		params.put("url", url);
//		params.put("create spatial index", Boolean.TRUE);

		ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
		newDataStore.createSchema(featureType);
//		newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);

		Transaction transaction = new DefaultTransaction("create");

		String typeName = newDataStore.getTypeNames()[0];
		FeatureStore<SimpleFeatureType, SimpleFeature> featureStore = (FeatureStore<SimpleFeatureType, SimpleFeature>) newDataStore.getFeatureSource(typeName);

		featureStore.setTransaction(transaction);

		try {
			FeatureCollection collection = FeatureCollections.newCollection();
			for (Feature feature : features) {
				SimpleFeature simpleFeature = feature.getAsSimpleFeature();
				collection.add(simpleFeature);
			}
			featureStore.addFeatures(collection);
			transaction.commit();
			LOG.info("written features to " + url);
		} catch (Exception e) {
			LOG.error(e, e);
			transaction.rollback();

		} finally {
			transaction.close();
		}

	}
	

}